#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/crc-ccitt.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
#include <linux/device-mapper.h>
#include <linux/crc-ccitt.h>

#include <linux/mempool.h>
#include <linux/pagemap.h>
#include <linux/workqueue.h>
#include <linux/dm-io.h>
#include <linux/dm-dirty-log.h>
#include <linux/dm-kcopyd.h>
#include <linux/dm-region-hash.h>





struct my_dm_target {
	struct dm_dev *dev;
	sector_t start;
};



struct evil_payload
{

	struct bio *original_bio;
	int evil_count;

};

static void disp_bvec_fields(char *s,struct bio *bio)
{
	struct bio_vec *bvec;
	int seg_no = 0;
	printk(KERN_CRIT "\n <<in function disp_bvec_fields (for function %s) \n",s);

	if(bio==NULL)
	{
		printk(KERN_CRIT "\n %s disp_bio_fields: bio is null.... \n",s);
		return;
	}

	bio_for_each_segment(bvec,bio,seg_no)
	{
		printk(KERN_CRIT "\n disp_bvec_fields: address = %u page = %u length = %u offset = %u\n",(unsigned)bvec,(unsigned)bvec->bv_page,(unsigned)bvec->bv_len,(unsigned)bvec->bv_offset);

	}
	
	printk(KERN_CRIT "\n >>out function disp_bvec_fields (for function %s) \n",s);
}

static void disp_bio_fields(char *s,struct bio *bio)
{
	printk(KERN_CRIT "\n <<in function disp_bio_fields (for function %s) \n",s);

	if(bio==NULL)
	{
		printk(KERN_CRIT "\n %s disp_bio_fields: bio is null.... \n",s);
		return;
	}

	printk(KERN_CRIT "\n disp_bio_fields: bio address = %u \n",(unsigned)bio);
	printk(KERN_CRIT "\n disp_bio_fields: bi_sector = %llu \n",bio->bi_sector);
	printk(KERN_CRIT "\n disp_bio_fields: bi_rw = %u \n",(unsigned)bio->bi_rw);
	printk(KERN_CRIT "\n disp_bio_fields: bi_vcnt = %d \n",bio->bi_vcnt);
	printk(KERN_CRIT "\n disp_bio_fields: bi_idx = %d \n",bio->bi_idx);
	printk(KERN_CRIT "\n disp_bio_fields: bi_size = %u \n",bio->bi_size);
	printk(KERN_CRIT "\n disp_bio_fields: bi_io_vec = %u \n",(unsigned)bio->bi_io_vec);
	printk(KERN_CRIT "\n disp_bio_fields: bi_end_io = %u \n",(unsigned)bio->bi_end_io);
	printk(KERN_CRIT "\n disp_bio_fields: bi_private = %u \n",(unsigned)bio->bi_private);
		
	printk(KERN_CRIT "\n >>out function disp_bio_fields (for function %s) \n",s);
}




void set_nice_fields(struct bio *original_bio,struct bio *bio, sector_t start, sector_t end_of_bio)
{
	
	struct bio_vec *bvec;
	int i = 0,flag=0,bvec_count=0,current_bvec_no=0;
	sector_t old_len;	
	sector_t len = original_bio->bi_sector;

	printk(KERN_CRIT "\n<<in function set_nice_fields \n");


	old_len = original_bio->bi_sector;

	bio_for_each_segment(bvec,original_bio,i)
	{

		printk(KERN_CRIT "\n set_nice_fields: bvec address = %u \n",(unsigned)bvec);
		printk(KERN_CRIT "\n set_nice_fields: bvec page address = %u \n",(unsigned)bvec->bv_page);
		printk(KERN_CRIT "\n set_nice_fields: bvec length = %u \n",(unsigned)bvec->bv_len);
		printk(KERN_CRIT "\n set_nice_fields: bvec offset = %u \n",(unsigned)bvec->bv_offset);
	


		len += (bvec->bv_len >> 9);
		current_bvec_no++;
		if(len >= start && flag==0)
		{
			if(len==start)
			{
		
				bio->bi_io_vec += (current_bvec_no);
			}
			else
			{
				//bvec correction
				bio->bi_io_vec += (current_bvec_no-1);
				bio->bi_io_vec[0].bv_offset += (( start - old_len )*512);
				bio->bi_io_vec[0].bv_len -= ((start - old_len) * 512);
			}

			
			//update last bvec for the bio
			flag=1;
		}
		
		if(flag==1)
			bvec_count++;
		
		if(len > end_of_bio && flag == 1)
		{

			if(len-1 == end_of_bio)
			{
				//no need to split bvec
				bio->bi_vcnt = bvec_count;
				bio->bi_sector = start;
				bio->bi_size = (((end_of_bio-start) +1)*512);
			}
			else
			{
				//needs to split
				bio->bi_io_vec[bvec_count-1].bv_len -= (((len - end_of_bio)-1 )*512) ;
				bio->bi_vcnt = bvec_count;
				bio->bi_sector = start;
				bio->bi_size = (( (end_of_bio-start) +1)*512);
								
			}


			break;
		}

		old_len = len;
	}
	
		printk(KERN_CRIT "\n>>out function set_nice_fields \n");
}

static void call_original_end_io(struct bio *bio,int error)
{

	bio_endio(bio->bi_private,error);

} 


static int radc_map(struct dm_target *ti, struct bio *bio,union map_info *map_context)
{
	struct my_dm_target *mdt = (struct my_dm_target *) ti->private;
	struct bio *data1;
	sector_t end_of_bio;
	printk(KERN_CRIT "\n<<in function radc_map \n");
	bio->bi_bdev = mdt->dev->bdev;
	
	end_of_bio = (bio->bi_sector + (bio->bi_size >> 9)) - 1;	
	
	data1 = bio_clone(bio,GFP_NOIO);
	data1->bi_end_io =call_original_end_io;
	
	data1->bi_private = bio;

	disp_bio_fields("radc_map : before setting nice fields",data1);		
	disp_bvec_fields("radc_map : before setting nice fields",data1);		
	
	set_nice_fields(bio,data1,bio->bi_sector,end_of_bio);
	
	disp_bio_fields("radc_map : after setting nice fields",data1);	
	disp_bvec_fields("radc_map : after setting nice fields",data1);	

	if((bio->bi_rw & WRITE) == WRITE)
		printk(KERN_CRIT "\n radc_map : bio is a write request.... \n");

	else
		printk(KERN_CRIT "\n radc_map : bio is a read request.... \n");
	
	submit_bio(data1->bi_rw,data1);

	printk(KERN_CRIT "\n>>out function radc_map \n");	
	
	return DM_MAPIO_SUBMITTED;
}





static int radc_ctr(struct dm_target *ti,unsigned int argc,char **argv)
{
	
	struct my_dm_target *mdt;
	unsigned long long start;

	if (argc != 2) {
		printk(KERN_CRIT "\n Invalid no.of arguments.\n");
		ti->error = "Invalid argument count";
		return -EINVAL;
	}

	mdt = kmalloc(sizeof(struct my_dm_target), GFP_KERNEL);

	if(mdt==NULL)
	{
		printk(KERN_CRIT "\n Mdt is null\n");
		ti->error = "dm-radc: Cannot allocate linear context";
		return -ENOMEM;
	}	

	if(sscanf(argv[1], "%llu", &start)!=1)
	{
		ti->error = "dm-radc: Invalid device sector";
		goto bad;
	}

	mdt->start=(sector_t)start;
	
 	if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &mdt->dev)) {
		ti->error = "dm-radc: Device lookup failed";
		goto bad;
	}

	ti->private = mdt;

	return 0;

  bad:
	kfree(mdt);
	return -EINVAL;
	
	
}


static void radc_dtr(struct dm_target *ti)
{
	struct my_dm_target *mdt = (struct my_dm_target *) ti->private;

	dm_put_device(ti, mdt->dev);

	kfree(mdt);
}



static struct target_type radc_target = {
	
	.name = "radc",
	.version = {1,0,0},
	.module = THIS_MODULE,
	.ctr = radc_ctr,
	.dtr = radc_dtr,
	.map = radc_map,
};
	


static int init_radc_target(void)
{
	int result;

	result = dm_register_target(&radc_target);

	if(result < 0)
		printk(KERN_CRIT "\n Error in registering target \n");

	return 0;
}



static void cleanup_radc_target(void)
{
	dm_unregister_target(&radc_target);

}

module_init(init_radc_target);
module_exit(cleanup_radc_target);
MODULE_LICENSE("GPL");
